import Taro from '@tarojs/taro';
import { EventEmitter } from './event';
import { RetryPolicy } from './retry';
import { connectSocket, offNetworkStatusChange } from '@tarojs/taro';

type WebSocketEvent = 'open' | 'message' | 'close' | 'error' | 'reconnecting';
interface WebSocketOptions {
    url: string;
    protocols?: string[];
    autoReconnect?: boolean; // 自动重连 (默认true)
    maxRetries?: number; // 最大重连次数 (默认5)
    initialDelay?: number; // 基础重连间隔ms (默认1000)
    maxDelay?: number; // 最大重连间隔ms (默认30000)
}

class WebSocket extends EventEmitter<WebSocketEvent> {
    private socketTask: Taro.SocketTask | null = null;
    private isManualClose = false;
    private currentRetries = 0;
    private options: Required<WebSocketOptions>;
    private retryPolicy: RetryPolicy;
    private retryTimer: any;
    private isReconnecting = false;
    constructor(options: WebSocketOptions) {
        super();
        this.options = {
            autoReconnect: true,
            maxRetries: 5,
            initialDelay: 1000,
            maxDelay: 30000,
            protocols: [],
            ...options
        };
        this.retryPolicy = new RetryPolicy({
            maxRetries: this.options.maxRetries,
            initialDelay: this.options.initialDelay,
            maxDelay: this.options.maxDelay
        });

        this.connect();
    }

    private scheduleRetry(): void {
        if (this.isReconnecting) return;
        if (this.isManualClose) return;
        const { shouldRetry, delay } = this.retryPolicy.calculate(this.currentRetries);
        if (!shouldRetry) {
            this.clearReconnectTimer();
            this.emit('reconnecting', { currentRetries: this.currentRetries, shouldRetry, delay });
            return;
        }
        this.currentRetries++;
        this.isReconnecting = true;

        this.emit('reconnecting', { currentRetries: this.currentRetries, shouldRetry, delay });
        this.retryTimer = setTimeout(() => {
            this.isReconnecting = false;
            this.connect();
        }, delay);
    }

    // 主连接方法
    async connect() {
        this.clearReconnectTimer();
        if (this.socketTask) {
            this.socketTask.onOpen = null;
            this.socketTask.onMessage = null;
            this.socketTask.onClose = null;
            this.socketTask.onError = null;
            this.socketTask.close();
            this.socketTask = null;
        }
        try {
            this.isReconnecting = false;
            this.socketTask = await connectSocket({
                url: this.options.url,
                protocols: this.options.protocols
            });
            this.setupSocketHandlers();
        } catch (err) {
            this.handleError(err);
        }
    }

    // 设置WebSocket事件处理器
    private setupSocketHandlers() {
        if (!this.socketTask) return;

        this.socketTask.onOpen(() => {
            this.currentRetries = 0;
            this.emit('open');
        });

        this.socketTask.onMessage((res) => {
            this.emit('message', res.data);
        });

        this.socketTask.onError((err) => {
            console.log('socket2 err', err);
            this.handleError(err);
        });

        this.socketTask.onClose((res) => {
            console.log('socket close', res);
            this.handleClose(res);
        });
    }

    // 错误处理
    private handleError(err: any) {
        this.emit('error', err);
        if (
            !this.isManualClose &&
            this.socketTask?.readyState !== this.socketTask?.CLOSED &&
            !this.isReconnecting
        ) {
            this.scheduleRetry();
        }
    }

    // 关闭处理
    private handleClose(res: Taro.SocketTask.OnCloseCallbackResult) {
        this.emit('close', res);
        if (res?.code === 1000) {
            console.log('Connection closed normally, no retry');
            return;
        }
        if (!this.isManualClose && this.options.autoReconnect && !this.isReconnecting) {
            this.scheduleRetry();
        }
    }

    // 公共方法
    public send(data: string | ArrayBuffer | object): boolean {
        if (!this.isConnected) {
            console.warn('WebSocket未连接');
            return false;
        }
        try {
            const payload = typeof data === 'object' ? JSON.stringify(data) : data;
            this.socketTask?.send({ data: payload });
            return true;
        } catch (err) {
            this.handleError(err);
            return false;
        }
    }

    public close(code = 1000, reason = 'manual close') {
        this.isManualClose = true;
        this.clearReconnectTimer();
        this.socketTask && this.socketTask.close({ code, reason });
    }

    public get isConnected(): boolean {
        return this.socketTask && this.socketTask.readyState === 1 ? true : false;
    }

    // 清理资源
    private clearReconnectTimer() {
        if (this.retryTimer) {
            clearTimeout(this.retryTimer);
            this.retryTimer = null;
        }
    }

    public destroy() {
        this.close();
        this.removeAllListeners();
        this.clearReconnectTimer();
        offNetworkStatusChange();
    }
}

export default WebSocket;
